%% @author Carlos Abalde <carlos.abalde@gmail.com>
%% @doc    ...

%%%----------------------------------------------------------------------
%%%
%%% EMC, Copyright (C) 2009 Carlos Abalde
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------

-module(emc.emc).

-import(lists).
-import(io_lib).
-import(erlang).
-import(emc.util.config).
-import(emc.util.util).
-import(emc.cpool).
-import(emc.connection).

-include("emc.hrl").

-define(TIMEOUT, 60000).

%% API
-export([set/3, set/4]).
-export([rset/3, rset/4]).
-export([get/1, get/2]).
-export([rget/1, rget/2]).
-export([mget/1, mget/2]).
-export([mrget/1, mrget/2]).
-export([delete/2, delete/3]).
-export([flush/1, flush/2]).
-export([increment/2, increment/3]).
-export([decrement/2, decrement/3]).
-export([lib_version/0, lib_version/1]).
-export([stats/0]).

%%%-----------------------------------------------------------------------------
%%% API
%%%-----------------------------------------------------------------------------

%%
%% Key:        string() | int() | ...
%% Value:      term()
%% Expiration: int() | null
%%
set(Key, Value, Expiration) ->
    set(Key, Value, Expiration, ?TIMEOUT).

set(Key, Value, Expiration, Timeout) ->
    ValueList = binary_to_list(term_to_binary(Value)),
    execute({set, util:a2l(Key), ValueList, length(ValueList), Expiration}, Timeout).

%%
%% Key:        string() | int() | ...
%% Value:      list()
%% Expiration: int() | null
%%
rset(Key, Value, Expiration) ->
    rset(Key, Value, Expiration, ?TIMEOUT).

rset(Key, Value, Expiration, Timeout) when is_list(Value) ->
    execute({set, util:a2l(Key), Value, length(Value), Expiration}, Timeout).

%%
%% Key: string() | int() | ...
%%
get(Key) ->
    get(Key, ?TIMEOUT).

get(Key, Timeout) ->
    case execute({get, util:a2l(Key)}, Timeout) of
	{ok, ValueList} -> {ok, binary_to_term(list_to_binary(ValueList))};
	_Else           -> _Else
    end.

%%
%% Key: string() | int() | ...
%%
rget(Key) ->
    rget(Key, ?TIMEOUT).

rget(Key, Timeout) ->
    execute({get, util:a2l(Key)}, Timeout).

%%
%% Keys: [string() | int() | ...]
%%
mget(Keys) ->
    mget(Keys, ?TIMEOUT).

mget(Keys, Timeout) ->
    case execute({mget, [ util:a2l(Key) || Key <- Keys ]}, Timeout) of
	{ok, Results} -> {ok, [ {Key, binary_to_term(list_to_binary(ValueList))} || {Key, ValueList} <- Results ]};
	_Else         -> _Else
    end.

%%
%% Keys: [string() | int() | ...]
%%
mrget(Keys) ->
    mrget(Keys, ?TIMEOUT).

mrget(Keys, Timeout) ->
    execute({mget, [ util:a2l(Key) || Key <- Keys ]}, Timeout).

%%
%% Key:        string() | int() | ...
%% Expiration: int() | null
%%
delete(Key, Expiration) ->
    delete(Key, Expiration, ?TIMEOUT).

delete(Key, Expiration, Timeout) ->
    execute({delete, util:a2l(Key), Expiration}, Timeout).

%%
%% Expiration: int() | null
%%
flush(Expiration) ->
    flush(Expiration, ?TIMEOUT).

flush(Expiration, Timeout) ->
    execute({flush, Expiration}, Timeout).

%%
%% Key:        string() | int() | ...
%% Offset:     int()
%%
increment(Key, Offset) ->
    increment(Key, Offset, ?TIMEOUT).

increment(Key, Offset, Timeout) ->
    execute({increment, util:a2l(Key), Offset}, Timeout).

%%
%% Key:        string() | int() | ...
%% Offset:     int()
%%
decrement(Key, Offset) ->
    decrement(Key, Offset, ?TIMEOUT).

decrement(Key, Offset, Timeout) ->
    execute({decrement, util:a2l(Key), Offset}, Timeout).

%%
%%
%%
lib_version() ->
    lib_version(?TIMEOUT).

lib_version(Timeout) ->
    execute({lib_version}, Timeout).

%%
%%
%%
stats() ->
    stats(?TIMEOUT).

stats(Timeout) ->
    execute({stats}, Timeout).

%%%-----------------------------------------------------------------------------
%%% Internal implementation
%%%-----------------------------------------------------------------------------

%%
%%
%%
execute(Command, Timeout) ->
    case cpool:connect() of
	{ok, ConnPid} ->
	    Result = connection:execute(ConnPid, Command, Timeout),
	    ok = cpool:disconnect(ConnPid),
	    Result;
	_Else ->
	    _Else
    end.
